#include <iostream>
#include <string>

#include <arpa/inet.h>
#include <cstdlib>

#include "Socket.hpp"
#include "MathProtocol.hpp"

class CalculatorClient{
public:
    CalculatorClient() : _connect(false){
        _clientSocket.Sock();
    }

    ~CalculatorClient(){ _disconnect(); }
    
    bool Connect(const std::string& IP, uint16_t port){
        _connect = _clientSocket.Connect(IP, port);
        return _connect;
    }

    //发送计算信息函数
    bool SendCalculation(MathProtocol::Operation op, double operand1, double operand2){
        if(!_connect) return false;
        
        auto request = MathProtocol::CreateRequest(op, operand1, operand2);

        if(send(_clientSocket.GetSocket(), request.data(), request.size(), 0) < 0 ){
            return false;
        }

        //获取返回消息
        uint8_t buffer[1024];
        size_t received = recv(_clientSocket.GetSocket(), buffer, sizeof(buffer), 0);

        if(received <= 0) return false;

        //解析头
        MathProtocol::MessageHeader header;
        if(!MathProtocol::ParseHeader(buffer, received, header)){
            return false;
        }

        switch(header.type){
            case MathProtocol::MessageType::RESPONSE : {
                MathProtocol::ResponseMessage response;
                if(MathProtocol::ParseResponse(buffer, received, response)){
                    if(response.errorCode == MathProtocol::ErrorCode::SUCCESS){
                        std::cout << "计算成功：" << operand1 << " " << MathProtocol::GetOperationName(op)
                        << " " << operand2 << " = " << response.result << std::endl;
                    }else{
                        std::cout << "计算错误：" << MathProtocol::GetErrorDescription(response.errorCode)
                        << std:: endl;
                    }
                    return true;
                }
                break;
            }
            case MathProtocol::MessageType::ERROR :{
                MathProtocol::ErrorMessage error;
                std::string errormsg;
                if(MathProtocol::ParseError(buffer, received, error, errormsg)){
                    std::cout << "服务器错误：" << errormsg << std::endl;
                    return true;
                }
                break;
            }
        }
        return false;
    }

    //启动客户端(分析客户端并且执行运算函数)
    void RunInteractive(){
        std::cout << "数学计算客户端(输入quit关闭)" << std::endl;
        std::cout << "输入格式: 数值 操作符 数值" << std::endl;

        std::string line;
        while(std::getline(std::cin, line)){
            if(line == "quit"){
                break;
            }

            double operand1, operand2;
            char opchar;
            if(sscanf(line.c_str(), "%lf %c %lf", &operand1, &opchar, &operand2) != 3){
                std::cout << "输入错误" << std::endl;
                continue;
            }

            //输入正确
            MathProtocol::Operation op;
            switch (opchar)
            {
            case '+':
                op = MathProtocol::Operation::ADD;
                break;
            case '-':
                op = MathProtocol::Operation::SUBTRACT;
                break;
            case '*':
                op = MathProtocol::Operation::MULTIPLY;
                break;
            case '/':
                op = MathProtocol::Operation::DIVIDE;
                break;
            default:
                std::cout << "未知操作" << std::endl;
                continue;
            }

            //进行运算
            SendCalculation(op, operand1, operand2);
        }
    }

private:
    bool _connect;
    TCPSocket _clientSocket;

    //关闭客户端
    void _disconnect(){
        _clientSocket.Close();
        _connect = false;
    }
};


int main(int argc, char* argv[]){
    if(argc != 3){
        std::cout << "CalculatorClient ServerIP Port" << std::endl;
        return 0;
    }

    std::string IP = argv[1];
    uint16_t port = static_cast<uint16_t>(std::stoi(argv[2]));
    
    std::cout << "IP:" << IP << ", Port:" << port << std::endl; 

    CalculatorClient client;
    if(!client.Connect(IP, port)){
        return 1;
    }

    client.RunInteractive();

    return 0;    
}